<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket语音聊天测试</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }
        .container {
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .status {
            padding: 10px;
            margin: 10px 0;
            border-radius: 5px;
            font-weight: bold;
        }
        .connected {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .disconnected {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .messages {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            padding: 10px;
            margin: 10px 0;
            background: #f9f9f9;
        }
        .message {
            margin: 5px 0;
            padding: 5px;
            border-left: 3px solid #007bff;
            background: white;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin: 10px 0;
        }
        button {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
        }
        .btn-primary {
            background: #007bff;
            color: white;
        }
        .btn-success {
            background: #28a745;
            color: white;
        }
        .btn-danger {
            background: #dc3545;
            color: white;
        }
        .btn-warning {
            background: #ffc107;
            color: #212529;
        }
        input[type="text"] {
            flex: 1;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .audio-controls {
            margin: 10px 0;
        }
        audio {
            width: 100%;
            margin: 10px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎤 WebSocket语音聊天测试</h1>
        
        <!-- 连接状态 -->
        <div id="status" class="status disconnected">
            ❌ WebSocket未连接
        </div>
        
        <!-- 控制按钮 -->
        <div class="controls">
            <button id="connectBtn" class="btn-primary" onclick="connectWebSocket()">连接WebSocket</button>
            <button id="disconnectBtn" class="btn-danger" onclick="disconnectWebSocket()" disabled>断开连接</button>
        </div>
        
        <!-- 消息显示区域 -->
        <div id="messages" class="messages"></div>
        
        <!-- 文本输入测试 -->
        <div class="controls">
            <input type="text" id="textInput" placeholder="输入测试消息..." onkeypress="handleKeyPress(event)">
            <button class="btn-success" onclick="sendTextMessage()">发送文本</button>
        </div>
        
        <!-- 录音控制 -->
        <div class="audio-controls">
            <button id="recordBtn" class="btn-warning" onclick="toggleRecording()">🎤 开始录音</button>
            <button id="playBtn" class="btn-primary" onclick="playLastAudio()" disabled>🔊 播放最后音频</button>
        </div>
        
        <!-- 音频播放器 -->
        <audio id="audioPlayer" controls style="display: none;"></audio>
    </div>

    <script>
        let ws = null;
        let isConnected = false;
        let mediaRecorder = null;
        let recordedChunks = [];
        let lastAudioBase64 = null;

        // 连接WebSocket
        function connectWebSocket() {
            try {
                ws = new WebSocket('ws://localhost:8123/ws/voice-chat');
                
                ws.onopen = function() {
                    isConnected = true;
                    updateStatus('✅ WebSocket已连接', 'connected');
                    addMessage('系统', 'WebSocket连接成功');
                    document.getElementById('connectBtn').disabled = true;
                    document.getElementById('disconnectBtn').disabled = false;
                };
                
                ws.onmessage = function(event) {
                    console.log('收到消息:', event.data);
                    try {
                        const data = JSON.parse(event.data);
                        handleWebSocketMessage(data);
                    } catch (error) {
                        console.error('解析消息失败:', error);
                        addMessage('错误', '消息解析失败: ' + error.message);
                    }
                };
                
                ws.onerror = function(error) {
                    console.error('WebSocket错误:', error);
                    addMessage('错误', 'WebSocket连接错误');
                };
                
                ws.onclose = function(event) {
                    isConnected = false;
                    updateStatus('❌ WebSocket已断开', 'disconnected');
                    addMessage('系统', `WebSocket连接关闭: ${event.code} - ${event.reason}`);
                    document.getElementById('connectBtn').disabled = false;
                    document.getElementById('disconnectBtn').disabled = true;
                };
                
            } catch (error) {
                console.error('连接失败:', error);
                addMessage('错误', '连接失败: ' + error.message);
            }
        }

        // 断开WebSocket
        function disconnectWebSocket() {
            if (ws) {
                ws.close();
            }
        }

        // 处理WebSocket消息
        function handleWebSocketMessage(data) {
            console.log('处理消息:', data);
            
            switch (data.type) {
                case 'connection':
                    addMessage('系统', `连接状态: ${data.message}`);
                    break;
                    
                case 'voice-response':
                    const response = data.data;
                    addMessage('AI回复', response.aiResponse || '无回复内容');
                    
                    if (response.audioBase64) {
                        lastAudioBase64 = response.audioBase64;
                        document.getElementById('playBtn').disabled = false;
                        addMessage('系统', `收到音频数据，长度: ${response.audioBase64.length}`);
                        
                        // 自动播放
                        setTimeout(() => playAudio(response.audioBase64), 500);
                    }
                    break;
                    
                case 'processing':
                    addMessage('系统', data.message);
                    break;
                    
                case 'error':
                    addMessage('错误', data.message);
                    break;
                    
                default:
                    addMessage('未知', JSON.stringify(data));
            }
        }

        // 发送文本消息
        function sendTextMessage() {
            const input = document.getElementById('textInput');
            const text = input.value.trim();
            
            if (!text) {
                alert('请输入消息内容');
                return;
            }
            
            if (!isConnected) {
                alert('WebSocket未连接');
                return;
            }
            
            const request = {
                textInput: text,
                sessionId: Date.now().toString()
            };
            
            try {
                ws.send(JSON.stringify(request));
                addMessage('用户', text);
                input.value = '';
            } catch (error) {
                console.error('发送失败:', error);
                addMessage('错误', '发送失败: ' + error.message);
            }
        }

        // 键盘事件处理
        function handleKeyPress(event) {
            if (event.key === 'Enter') {
                sendTextMessage();
            }
        }

        // 录音功能
        async function toggleRecording() {
            const recordBtn = document.getElementById('recordBtn');
            
            if (!mediaRecorder || mediaRecorder.state === 'inactive') {
                try {
                    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                    mediaRecorder = new MediaRecorder(stream);
                    recordedChunks = [];
                    
                    mediaRecorder.ondataavailable = function(event) {
                        if (event.data.size > 0) {
                            recordedChunks.push(event.data);
                        }
                    };
                    
                    mediaRecorder.onstop = function() {
                        const blob = new Blob(recordedChunks, { type: 'audio/webm' });
                        convertBlobToBase64(blob);
                    };
                    
                    mediaRecorder.start();
                    recordBtn.textContent = '⏹️ 停止录音';
                    recordBtn.className = 'btn-danger';
                    addMessage('系统', '开始录音...');
                    
                } catch (error) {
                    console.error('录音失败:', error);
                    addMessage('错误', '录音失败: ' + error.message);
                }
            } else {
                mediaRecorder.stop();
                recordBtn.textContent = '🎤 开始录音';
                recordBtn.className = 'btn-warning';
                addMessage('系统', '录音结束');
            }
        }

        // 将Blob转换为Base64
        function convertBlobToBase64(blob) {
            const reader = new FileReader();
            reader.onload = function() {
                const base64 = reader.result.split(',')[1]; // 移除data:audio/webm;base64,前缀
                sendVoiceMessage(base64);
            };
            reader.readAsDataURL(blob);
        }

        // 发送语音消息
        function sendVoiceMessage(audioBase64) {
            if (!isConnected) {
                alert('WebSocket未连接');
                return;
            }
            
            const request = {
                audioBase64: audioBase64,
                sessionId: Date.now().toString()
            };
            
            try {
                ws.send(JSON.stringify(request));
                addMessage('用户', `🎤 语音消息 (${audioBase64.length} 字符)`);
            } catch (error) {
                console.error('发送语音失败:', error);
                addMessage('错误', '发送语音失败: ' + error.message);
            }
        }

        // 将PCM数据转换为WAV格式
        function pcmToWav(pcmData, sampleRate = 24000, channels = 1, bitDepth = 16) {
            const length = pcmData.length;
            const buffer = new ArrayBuffer(44 + length);
            const view = new DataView(buffer);
            
            // WAV文件头
            const writeString = (offset, string) => {
                for (let i = 0; i < string.length; i++) {
                    view.setUint8(offset + i, string.charCodeAt(i));
                }
            };
            
            // RIFF标识符
            writeString(0, 'RIFF');
            view.setUint32(4, 36 + length, true); // 文件长度
            writeString(8, 'WAVE');
            
            // fmt子块
            writeString(12, 'fmt ');
            view.setUint32(16, 16, true); // fmt子块长度
            view.setUint16(20, 1, true); // 音频格式 (PCM)
            view.setUint16(22, channels, true); // 声道数
            view.setUint32(24, sampleRate, true); // 采样率
            view.setUint32(28, sampleRate * channels * bitDepth / 8, true); // 字节率
            view.setUint16(32, channels * bitDepth / 8, true); // 块对齐
            view.setUint16(34, bitDepth, true); // 位深度
            
            // data子块
            writeString(36, 'data');
            view.setUint32(40, length, true); // 数据长度
            
            // 复制PCM数据
            const dataView = new Uint8Array(buffer, 44);
            dataView.set(pcmData);
            
            return buffer;
        }

        // 检测是否有音频文件头
        function hasAudioHeader(bytes) {
            if (bytes.length < 4) return false;
            
            // 检查常见音频格式的文件头
            const header = Array.from(bytes.slice(0, 4)).map(b => String.fromCharCode(b)).join('');
            
            // WAV: RIFF
            if (header === 'RIFF') return true;
            
            // MP3: ID3 或 0xFF 0xFB
            if (header.startsWith('ID3') || (bytes[0] === 0xFF && (bytes[1] & 0xE0) === 0xE0)) return true;
            
            // OGG: OggS
            if (header === 'OggS') return true;
            
            return false;
        }

        // 播放音频
        async function playAudio(base64Data) {
            try {
                console.log('开始播放音频，数据长度:', base64Data.length);
                
                // 将Base64转换为字节数组
                const binaryString = atob(base64Data);
                const bytes = new Uint8Array(binaryString.length);
                for (let i = 0; i < binaryString.length; i++) {
                    bytes[i] = binaryString.charCodeAt(i);
                }
                
                let audioBuffer;
                let mimeType;
                
                // 检测是否为PCM数据（阿里云TTS返回的格式）
                if (!hasAudioHeader(bytes)) {
                    console.log('检测到PCM音频数据，转换为WAV格式');
                    audioBuffer = pcmToWav(bytes, 24000, 1, 16); // 阿里云TTS的参数
                    mimeType = 'audio/wav';
                } else {
                    audioBuffer = bytes.buffer;
                    mimeType = 'audio/wav'; // 默认尝试WAV
                }
                
                const blob = new Blob([audioBuffer], { type: mimeType });
                const audioUrl = URL.createObjectURL(blob);
                
                const audio = new Audio();
                
                return new Promise((resolve, reject) => {
                    audio.oncanplay = function() {
                        audio.play()
                            .then(() => {
                                addMessage('系统', `音频播放成功，格式: ${mimeType}`);
                                resolve(true);
                            })
                            .catch((error) => {
                                URL.revokeObjectURL(audioUrl);
                                reject(error);
                            });
                    };
                    
                    audio.onerror = function(error) {
                        URL.revokeObjectURL(audioUrl);
                        reject(error);
                    };
                    
                    audio.src = audioUrl;
                });
                
            } catch (error) {
                console.error('播放音频失败:', error);
                addMessage('错误', '播放音频失败: ' + error.message);
            }
        }

        // 尝试播放特定格式的音频
        function tryPlayAudio(base64Data, mimeType) {
            return new Promise((resolve, reject) => {
                try {
                    const binaryString = atob(base64Data);
                    const bytes = new Uint8Array(binaryString.length);
                    for (let i = 0; i < binaryString.length; i++) {
                        bytes[i] = binaryString.charCodeAt(i);
                    }
                    
                    const blob = new Blob([bytes], { type: mimeType });
                    const audioUrl = URL.createObjectURL(blob);
                    
                    const audio = new Audio();
                    
                    audio.oncanplay = function() {
                        audio.play()
                            .then(() => {
                                console.log(`音频播放成功，格式: ${mimeType}`);
                                resolve(true);
                            })
                            .catch((error) => {
                                URL.revokeObjectURL(audioUrl);
                                resolve(false);
                            });
                    };
                    
                    audio.onerror = function(error) {
                        URL.revokeObjectURL(audioUrl);
                        resolve(false);
                    };
                    
                    audio.src = audioUrl;
                    
                } catch (error) {
                    resolve(false);
                }
            });
        }

        // 播放最后的音频
        function playLastAudio() {
            if (lastAudioBase64) {
                playAudio(lastAudioBase64);
            } else {
                alert('没有音频可播放');
            }
        }

        // 更新连接状态
        function updateStatus(text, className) {
            const status = document.getElementById('status');
            status.textContent = text;
            status.className = `status ${className}`;
        }

        // 添加消息到显示区域
        function addMessage(sender, content) {
            const messages = document.getElementById('messages');
            const messageDiv = document.createElement('div');
            messageDiv.className = 'message';
            messageDiv.innerHTML = `
                <strong>[${new Date().toLocaleTimeString()}] ${sender}:</strong> ${content}
            `;
            messages.appendChild(messageDiv);
            messages.scrollTop = messages.scrollHeight;
        }

        // 页面加载完成后自动连接
        window.onload = function() {
            addMessage('系统', '页面加载完成，点击连接WebSocket按钮开始测试');
        };
    </script>
</body>
</html>
